const obj = {};
const obj2 = {
  name: "张三",
  info: {
    age: 18,
    sex: "男",
  },
};
// Object.entries(obj):返回空数组,不包括不可枚举属性----性能最好
console.log(Object.keys(obj)); // []
console.log(Object.keys(obj2)); // ['name', 'info']
console.log(JSON.stringify(obj), JSON.stringify(obj) === "{}"); // {}
console.log(JSON.stringify(obj2), JSON.stringify(obj2) === "{}"); // {}

//Object.getOwnPropertyNames(obj)：返回空数组,包括不可枚举属性
console.log(Object.getOwnPropertyNames(obj)); //[]  
console.log(Object.getOwnPropertyNames(obj2)); 

// Reflect.ownKeys(obj):返回空数组,包括不可枚举属性和symbol属性
console.log('Reflect.ownKeys(obj)',Reflect.ownKeys(obj)); //[]   
console.log('Reflect.ownKeys(obj2)',Reflect.ownKeys(obj2)); //['name', 'info']

// Object.entries(obj):返回空数组,不包括不可枚举属性
console.log('Object.entries(obj)',Object.entries(obj)); //[]   
console.log('Object.entries(obj2)',Object.entries(obj2)); //[]   
//配合hasOwnProperty，只考虑对象自身的可枚举属性，而不考虑原型链上的属性。
function isEmpty(obj) {
  for (let key in obj) {
    console.log('key',key,obj.hasOwnProperty(key));//
    
    if (obj.hasOwnProperty(key)) {
      return false;
    }
  }
  return true;
}

if (isEmpty(obj)) {
  console.log("对象为空");
}
if (!isEmpty(obj2)) {
  console.log("对象不为空");
}

//不可枚举属性特点：在所有引用类型中都存在，不仅仅是对象和数组。
    // 它们不会在for...in循环中出现
    // 它们不会被Object.keys()方法返回
    // 它们不会被JSON.stringify()序列化
    // 它们不会出现在对象的扩展运算符...结果中
    //可以通过obj.xxx访问拿到
// 设置不可枚举属性的方式有两种：
    //1.使用Object.defineProperty()方法
    //2.使用Object.create(null)创建一个空对象，该对象没有继承任何属性
    // 普通对象的不可枚举属性
Object.defineProperty(obj, 'hiddenProp', {
  value: 'I am hidden',
  enumerable: false  // 设置为不可枚举
});

console.log(obj.hiddenProp);  // "I am hidden"
console.log(Object.keys(obj));  // [] (不包含hiddenProp)

// 数组的不可枚举属性：通过arr.xxx属性名拿到
const arr = [1, 2, 3];
Object.defineProperty(arr, 'specialItem', {
  value: 'special',
  enumerable: false
});

console.log(arr.specialItem);  // "special"
console.log(Object.keys(arr));  // ["0", "1", "2"] (不包含specialItem)

// 函数的不可枚举属性：
function myFunc() {}
Object.defineProperty(myFunc, 'description', {
  value: 'This is my function',
  enumerable: false
});

console.log(myFunc.description);  // "This is my function"
console.log(Object.keys(myFunc));  // [] (不包含description)


// 内置的不可枚举属性

// JavaScript中许多内置对象的原型方法都是不可枚举的，例如：

// Array.prototype.forEach
// Object.prototype.toString
// Function.prototype.call
// 这就是为什么当你使用for...in循环遍历数组时，不会看到这些方法。

// hasOwnProperty会检查所有自有属性，无论它们是否可枚举：(hasOwnProperty可以帮助避免原型污染攻击：)
// 返回布尔值，用于检查一个对象是否有特定的自有属性（非继承属性）
const obj = {
  a: 1,
  b: 2
};

for (let key in obj) {
  if (obj.hasOwnProperty(key)) {
    console.log(key, obj[key]);  // 只输出自有属性
  }
}
Object.defineProperty(obj, 'hidden', {
  value: 'secret',
  enumerable: false
});

console.log(obj.hasOwnProperty('hidden'));  // true
console.log('hidden' in obj);  // true (in操作符也会检查继承属性)
console.log(Object.keys(obj).includes('hidden'));  // false (Object.keys只返回可枚举属性)




// 下一个知识点：
//对象和数组的循环方法
//Object.defineProperty(obj, 'name', {value:'xxx',enumerable:false})的应用
//arguments对象的不可枚举属性